//
//  NSDate+Utilities.m
//  CodeBox
//
//  Created by 杨兴军 on 14-9-26.
//  Copyright (c) 2014年 PhotoStudio Inc. All rights reserved.
//

#import "NSDate+Utilities.h"

#define DATE_COMPONENTS (NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSWeekCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSWeekdayCalendarUnit | NSWeekdayOrdinalCalendarUnit)
#define CURRENT_CALENDAR [NSCalendar currentCalendar]

@implementation NSDate (UtilitiesExtensions)

#pragma mark - Relative Dates

+ (NSDate *)dateWithDaysFromNow:(NSUInteger)days
{
    NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_DAY * days;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithDaysFromDate:(NSDate *)date days:(NSUInteger)days
{
    NSTimeInterval aTimeInterval = [date timeIntervalSinceReferenceDate] + D_DAY * days;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithDaysBeforeNow:(NSUInteger)days
{
    NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] - D_DAY * days;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithDaysBeforeDate:(NSDate *)date days:(NSUInteger)days
{
    NSTimeInterval aTimeInterval = [date timeIntervalSinceReferenceDate] - D_DAY * days;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateTomorrow
{
    return [NSDate dateWithDaysFromNow:1];
}

+ (NSDate *)dateYesterday
{
    return [NSDate dateWithDaysBeforeNow:1];
}

+ (NSDate *)dateWithHoursFromNow:(NSUInteger)dHours
{
    NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_HOUR * dHours;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithDaysFromDate:(NSDate *)date hours:(NSUInteger)dHours
{
    NSTimeInterval aTimeInterval = [date timeIntervalSinceReferenceDate] + D_HOUR * dHours;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithHoursBeforeNow:(NSUInteger)dHours
{
    NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] - D_HOUR * dHours;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithDaysBeforeDate:(NSDate *)date hours:(NSUInteger)dHours
{
    NSTimeInterval aTimeInterval = [date timeIntervalSinceReferenceDate] - D_HOUR * dHours;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithMinutesFromNow:(NSUInteger)dMinutes
{
    NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_MINUTE * dMinutes;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithDaysFromDate:(NSDate *)date minutes:(NSUInteger)dMinutes
{
    NSTimeInterval aTimeInterval = [date timeIntervalSinceReferenceDate] + D_MINUTE * dMinutes;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithMinutesBeforeNow:(NSUInteger)dMinutes
{
    NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] - D_MINUTE * dMinutes;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

+ (NSDate *)dateWithDaysBeforeDate:(NSDate *)date minutes:(NSUInteger)dMinutes
{
    NSTimeInterval aTimeInterval = [date timeIntervalSinceReferenceDate] - D_MINUTE * dMinutes;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

#pragma mark -
#pragma mark Comparing Dates

- (BOOL)isEqualToDateIgnoringTime:(NSDate *)aDate
{
    NSDateComponents* components1 = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                        fromDate:self];
    NSDateComponents* components2 = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                        fromDate:aDate];
    return (([components1 year] == [components2 year]) && ([components1 month] == [components2 month]) && ([components1 day] == [components2 day]));
}

- (BOOL)isToday
{
    return [self isEqualToDateIgnoringTime:[NSDate date]];
}

- (BOOL)isTomorrow
{
    return [self isEqualToDateIgnoringTime:[NSDate dateTomorrow]];
}

- (BOOL)isYesterday
{
    return [self isEqualToDateIgnoringTime:[NSDate dateYesterday]];
}

// This hard codes the assumption that a week is 7 days
- (BOOL)isSameWeekAsDate:(NSDate *)aDate
{
    NSDateComponents* components1 = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                        fromDate:self];
    NSDateComponents* components2 = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                        fromDate:aDate];
    
    // Must be same week. 12/31 and 1/1 will both be week "1" if they are in the same week
    if ([components1 weekOfYear] != [components2 weekOfYear])
        return NO;
    
    // Must have a time interval under 1 week. Thanks @aclark
    return (fabs([self timeIntervalSinceDate:aDate]) < D_WEEK);
}

- (BOOL)isThisWeek
{
    return [self isSameWeekAsDate:[NSDate date]];
}

- (BOOL)isNextWeek
{
    NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_WEEK;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return [self isSameYearAsDate:newDate];
}

- (BOOL)isLastWeek
{
    NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] - D_WEEK;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return [self isSameYearAsDate:newDate];
}

- (BOOL)isSameYearAsDate:(NSDate *)aDate
{
    NSDateComponents* components1 = [CURRENT_CALENDAR components:NSYearCalendarUnit
                                                        fromDate:self];
    NSDateComponents* components2 = [CURRENT_CALENDAR components:NSYearCalendarUnit
                                                        fromDate:aDate];
    return ([components1 year] == [components2 year]);
}

- (BOOL)isThisYear
{
    return [self isSameWeekAsDate:[NSDate date]];
}

- (BOOL)isNextYear
{
    NSDateComponents* components1 = [CURRENT_CALENDAR components:NSYearCalendarUnit
                                                        fromDate:self];
    NSDateComponents* components2 = [CURRENT_CALENDAR components:NSYearCalendarUnit
                                                        fromDate:[NSDate date]];
    
    return ([components1 year] == ([components2 year] + 1));
}

- (BOOL)isLastYear
{
    NSDateComponents* components1 = [CURRENT_CALENDAR components:NSYearCalendarUnit
                                                        fromDate:self];
    NSDateComponents* components2 = [CURRENT_CALENDAR components:NSYearCalendarUnit
                                                        fromDate:[NSDate date]];
    
    return ([components1 year] == ([components2 year] - 1));
}

- (BOOL)isEarlierThanDate:(NSDate*)aDate
{
    return ([self earlierDate:aDate] == self);
}

- (BOOL)isLaterThanDate:(NSDate*)aDate
{
    return ([self laterDate:aDate] == self);
}

#pragma mark - Adjusting Dates

- (NSDate *)dateByAddingDays:(NSUInteger)dDays
{
    NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + D_DAY * dDays;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

- (NSDate *)dateBySubtractingDays:(NSUInteger)dDays
{
    return [self dateByAddingDays:(dDays * -1)];
}

- (NSDate *)dateByAddingHours:(NSUInteger)dHours
{
    NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + D_HOUR * dHours;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

- (NSDate *)dateBySubtractingHours:(NSUInteger)dHours
{
    return [self dateByAddingHours:(dHours * -1)];
}

- (NSDate *)dateByAddingMinutes:(NSUInteger)dMinutes
{
    NSTimeInterval aTimeInterval = [self timeIntervalSinceReferenceDate] + D_MINUTE * dMinutes;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    return newDate;
}

- (NSDate *)dateBySubtractingMinutes:(NSUInteger)dMinutes
{
    return [self dateByAddingMinutes:(dMinutes * -1)];
}

- (NSDate *)dateAtStartOfDay
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    [components setHour:0];
    [components setMinute:0];
    [components setSecond:0];
    return [CURRENT_CALENDAR dateFromComponents:components];
}

- (NSDateComponents *)componentsWithOffsetFromDate:(NSDate *)aDate
{
    NSDateComponents* dTime = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                  fromDate:aDate
                                                    toDate:self
                                                   options:0];
    return dTime;
}

#pragma mark - Retrieving Intervals

- (NSInteger)minutesAfterDate:(NSDate *)aDate
{
    NSTimeInterval ti = [self timeIntervalSinceDate:aDate];
    return (NSInteger)(ti / D_MINUTE);
}

- (NSInteger)minutesBeforeDate:(NSDate *)aDate
{
    NSTimeInterval ti = [aDate timeIntervalSinceDate:self];
    return (NSInteger)(ti / D_MINUTE);
}

- (NSInteger)hoursAfterDate:(NSDate *)aDate
{
    NSTimeInterval ti = [self timeIntervalSinceDate:aDate];
    return (NSInteger)(ti / D_HOUR);
}

- (NSInteger)hoursBeforeDate:(NSDate *)aDate
{
    NSTimeInterval ti = [aDate timeIntervalSinceDate:self];
    return (NSInteger)(ti / D_HOUR);
}

- (NSInteger)daysAfterDate:(NSDate *)aDate
{
    NSTimeInterval ti = [self timeIntervalSinceDate:aDate];
    return (NSInteger)(ti / D_DAY);
}

- (NSInteger)daysBeforeDate:(NSDate *)aDate
{
    NSTimeInterval ti = [aDate timeIntervalSinceDate:self];
    return (NSInteger)(ti / D_DAY);
}

#pragma mark - Decomposing Dates

- (NSInteger)nearestHour
{
    NSTimeInterval aTimeInterval = [[NSDate date] timeIntervalSinceReferenceDate] + D_MINUTE * 30;
    NSDate* newDate = [NSDate dateWithTimeIntervalSinceReferenceDate:aTimeInterval];
    NSDateComponents* components = [CURRENT_CALENDAR components:NSHourCalendarUnit
                                                       fromDate:newDate];
    return [components hour];
}

- (NSInteger)hour
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components hour];
}

- (NSInteger)minute
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components minute];
}

- (NSInteger)seconds
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components second];
}

- (NSInteger)day
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components day];
}

- (NSInteger)month
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components month];
}

- (NSInteger)weekOfMonth
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components weekOfMonth];
}

- (NSInteger)weekOfYear
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components weekOfYear];
}

- (NSInteger)weekday
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components weekday];
}

- (NSInteger)nthWeekday // e.g. 2nd Tuesday of the month is 2
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components weekdayOrdinal];
}

- (NSInteger)year
{
    NSDateComponents* components = [CURRENT_CALENDAR components:DATE_COMPONENTS
                                                       fromDate:self];
    return [components year];
}

@end

